#include "PCMPlayer.h"

PCMPlayer::PCMPlayer() {
    PerformADrop = false;

	unsigned int rc = 0;
	int rc2 = 0;
	snd_pcm_uframes_t Frames;

	if (snd_pcm_open(&Handle, "default", SND_PCM_STREAM_PLAYBACK, 0) < 0)
	    throw Error("PCMPlayer.cpp", "Initialization error", std::string("Unable to open PCM device: ") + std::string(snd_strerror(rc)) + ".");
	snd_pcm_hw_params_alloca(&DeviceParams);
	snd_pcm_hw_params_any(Handle, DeviceParams);
	snd_pcm_hw_params_set_access(Handle, DeviceParams, SND_PCM_ACCESS_RW_NONINTERLEAVED);

	BitFormat = ChooseFormat(OtterSDK->GetBitScaling());
	snd_pcm_hw_params_set_channels(Handle, DeviceParams, OtterSDK->GetChannels());
	Error err = ChangeFrequency(OtterSDK->GetSamplingFrequency());
	if (!err.Success())
	    throw err;
	snd_pcm_hw_params_get_period_size(DeviceParams, &Frames, 0);

	FramesAmount = Frames;
	PCMBufferSize = FramesAmount*OtterSDK->GetChannels()*(BitFormat/8);

	OtterSDK->SuggestPCMBufferSize(PCMBufferSize);
	PCMBuffer = new char[PCMBufferSize];

	snd_pcm_nonblock(Handle, 0);
}

void PCMPlayer::Play() {
	int ret;
	int PositionInBuffer = 0;
	OtterSDK->ReportPCMState(true);

	if (!OtterSDK->CodecPCMSignalAndWait()) {
		OtterSDK->ReportPCMState(false);
		return;
	}
	snd_pcm_prepare(Handle);
	snd_pcm_reset(Handle);
	while (true)
	{
	    if (!PerformADrop) {
            memcpy(PCMBuffer, OtterSDK->GetPCMBuffer() + PositionInBuffer, PCMBufferSize);
            ret = snd_pcm_writei(Handle, PCMBuffer, FramesAmount);
            if (ret < 0)
                snd_pcm_recover(Handle, ret, 0);

            PositionInBuffer += PCMBufferSize;
            if ((!PerformADrop) && ((PositionInBuffer + PCMBufferSize) > OtterSDK->GetPCMBufferSize())) {
                PositionInBuffer = 0;
                if (!OtterSDK->CodecPCMSignalAndWait()) {
                	OtterSDK->ReportPCMState(false);
                    return;
                }
            }
	    } else {
            snd_pcm_drop(Handle);
            snd_pcm_prepare(Handle);
            snd_pcm_reset(Handle);
            PerformADrop = false;
            if (!OtterSDK->CodecPCMSignalAndWait()) {
            	OtterSDK->ReportPCMState(false);
                return;
            }
	    }
	}
	OtterSDK->ReportPCMState(false);
}
